/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package zaizai.com.slice;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 水波纹button
 */
public class RippleView extends Button implements Component.DrawTask, Component.TouchEventListener {
    private static final String TAG = RippleView.class.getSimpleName();
    /**
     * 起始点
     */
    private int mDownX;
    private int mDownY;
    private float mDensity;
    /**
     * 绘制的半径
     */
    private float mDrawRadius;
    private float mStepRadius;
    private boolean mDrawFinish;
    private float mCycle;
    private final Rect mRect = new Rect();
    private boolean mPressUp = false;
    private Paint mRevealPaint;
    private int mRippleColor = Color.BLACK.getValue();
    private float mAlphaFactor = 0.2f;
    private AnimatorValue mRadiusAnimator;
    private int mDuration = 100;
    private boolean isPointMove = false;
    private ClickedListener clickListener;
    private boolean animateEnd = true;

    private static final int DRAW_RADIUS_ZERO = 0;
    private static final int DRAW_RADIUS_MOVE = 50;
    private static final int DURATION = 100;
    private static final int FREQUENCY = 10;


    /**
     * Constructor
     *
     * @param context 上下文
     */
    public RippleView(Context context) {
        this(context, null);
    }

    /**
     * Constructor
     *
     * @param context 上下文
     * @param attrSet 属性
     */
    public RippleView(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    /**
     * Constructor
     *
     * @param context   上下文
     * @param attrSet   属性
     * @param styleName 样式名
     */
    public RippleView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initView();
        setTouchEventListener(this);
        addDrawTask(this);
        setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                mRect.set(getLeft(), getTop(), getRight(), getBottom());
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
            }
        });
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

    /**
     * 初始化
     */
    private void initView() {
        mCycle = (float) DURATION / FREQUENCY;
        mDensity = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().densityPixels;
        mCycle = (mDensity * mCycle);
        mDrawFinish = true;

        mRevealPaint = new Paint();
        mRevealPaint.setAntiAlias(true);
        mRevealPaint.setColor(new Color(adjustAlpha(mRippleColor, mAlphaFactor)));

        mRadiusAnimator = new AnimatorValue();
        mRadiusAnimator.setDuration(mDuration);
        mRadiusAnimator.setLoopedCount(0);
        mRadiusAnimator.setCurveType(AnimatorValue.CurveType.LINEAR);
        mRadiusAnimator.setValueUpdateListener((animatorValue, value) -> {
            invalidate();
        });
        mRadiusAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                animateEnd = false;
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
                animateEnd = true;
            }

            @Override
            public void onEnd(Animator animator) {
                animateEnd = true;
                if (mPressUp) {
                    setDrawRadius(DRAW_RADIUS_ZERO);
                    mDrawFinish = true;
                }
                if (mPressUp && getClickedListener() != null) {
                    getClickedListener().onClick(RippleView.this);
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    private void startAnimation() {
        mRadiusAnimator.stop();
        mRadiusAnimator.start();
    }

    private void updateDrawData() {
        setDrawRadius(DRAW_RADIUS_ZERO);
        /*计算最大半径*/
        int radiusLeftTop = (int) Math.sqrt((double) mDownX * mDownX + mDownY * mDownY);
        int radiusRightTop = (int) Math.sqrt((double) (mRect.right - (mDownX + getLeft())) *
                (mRect.right - (mDownX + getLeft())) +
                mDownY * mDownY);
        int radiusLeftBottom = (int) Math.sqrt((double) mDownX * mDownX +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        int radiusRightBottom = (int) Math.sqrt((double) (mRect.right - (mDownX + getLeft())) *
                (mRect.right - (mDownX + getLeft())) +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        int[] radius = new int[]{radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom};
        int mMaxRadius = getMax(radius);

        mStepRadius = mMaxRadius / mCycle;
    }

    private int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = red(color);
        int green = green(color);
        int blue = blue(color);
        return Color.argb(alpha, red, green, blue);
    }

    private int vp(int vp) {
        return (int) (vp * mDensity + 0.5d);
    }

    /**
     * 设置绘画圆角半径
     *
     * @param radius 半径
     */
    public void setDrawRadius(final float radius) {
        mDrawRadius = radius;
    }

    @Override
    public void setClickedListener(ClickedListener listener) {
        this.clickListener = listener;
    }

    @Override
    public ClickedListener getClickedListener() {
        return clickListener;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        int action = touchEvent.getAction();
        isPointMove = false;
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN: {
                startAnimation();
                mPressUp = false;
                mDrawFinish = false;
                mDownX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mDownY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                updateDrawData();
                mStepRadius = (int) (5 * mStepRadius);
                break;
            }
            case TouchEvent.POINT_MOVE:
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mPressUp = true;
                if (mDrawFinish) {
                    mDrawFinish = false;
                    setDrawRadius(vp(DRAW_RADIUS_MOVE));
                }
                if (animateEnd) {
                    invalidate();
                }
                if (animateEnd && clickListener != null)
                    clickListener.onClick(RippleView.this);
                break;
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mDrawFinish) {
            return;
        }
        if (mStepRadius == 0) {
            return;
        }
        if (!isPointMove) {
            mDrawRadius = (float) ((double) mDrawRadius + (double) mStepRadius);
        }
        if (animateEnd && mPressUp) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            mDrawFinish = true;
        }
        canvas.drawCircle(mDownX, mDownY, mDrawRadius, mRevealPaint);
    }

    /**
     * 获取最大值
     *
     * @param radius 半径
     * @return int
     */
    private int getMax(int[] radius) {
        if (radius.length == 0) {
            return 0;
        }
        int max = radius[0];
        for (int mRadius : radius) {
            if (mRadius > max) {
                max = mRadius;
            }
        }
        return max;
    }

    private int red(int color) {
        return (color >> 16) & 0xFF;
    }

    private int green(int color) {
        return (color >> 8) & 0xFF;
    }

    private int blue(int color) {
        return color & 0xFF;
    }

}
